
%{

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#include "common/log/log.h"
#include "common/lang/string.h"
#include "sql/parser/parse_defs.h"
#include "sql/parser/yacc_sql.hpp"
#include "sql/parser/lex_sql.h"
#include "sql/expr/expression.h"

using namespace std;

string token_name(const char *sql_string, YYLTYPE *llocp)
{
  return string(sql_string + llocp->first_column, llocp->last_column - llocp->first_column + 1);
}

int yyerror(YYLTYPE *llocp, const char *sql_string, ParsedSqlResult *sql_result, yyscan_t scanner, const char *msg)
{
  std::unique_ptr<ParsedSqlNode> error_sql_node = std::make_unique<ParsedSqlNode>(SCF_ERROR);
  error_sql_node->error.error_msg = msg;
  error_sql_node->error.line = llocp->first_line;
  error_sql_node->error.column = llocp->first_column;
  sql_result->add_sql_node(std::move(error_sql_node));
  return 0;
}

ArithmeticExpr *create_arithmetic_expression(ArithmeticExpr::Type type,
                                             Expression *left,
                                             Expression *right,
                                             const char *sql_string,
                                             YYLTYPE *llocp)
{
  ArithmeticExpr *expr = new ArithmeticExpr(type, left, right);
  expr->set_name(token_name(sql_string, llocp));
  return expr;
}

%}

%define api.pure full
%define parse.error verbose
/** 启用位置标识 **/
%locations
%lex-param { yyscan_t scanner }
/** 这些定义了在yyparse函数中的参数 **/
%parse-param { const char * sql_string }
%parse-param { ParsedSqlResult * sql_result }
%parse-param { void * scanner }

//标识tokens
%token  SEMICOLON
        CREATE
        DROP
        TABLE
        TABLES
        UNIQUE
        INDEX
        CALC
        SELECT
        DESC
        SHOW
        SYNC
        INSERT
        DELETE
        UPDATE
        LBRACE
        RBRACE
        COMMA
        TRX_BEGIN
        TRX_COMMIT
        TRX_ROLLBACK
        INT_T
        STRING_T
        FLOAT_T
        DATE_T
        TEXT_T
        HELP
        EXIT
        DOT //QUOTE
        INTO
        VALUES
        FROM
        WHERE
        AND
        SET
        ON
        USE
        LOAD
        DATA
        INFILE
        EXPLAIN
        LIKE
        NOT
        IN
        EXISTS
        AVG
        MAX
        MIN
        SUM
        COUNT
        INNER
        JOIN
        NULLABLE
        ASC
        GRANT
        TO
        ORDER
        BY
        IS
        AS
        EQ
        LT
        GT
        LE
        GE
        NE

/** union 中定义各种数据类型，真实生成的代码也是union类型，所以不能有非POD类型的数据 **/
%union {
  ParsedSqlNode *                       sql_node;
  ConditionSqlNode *                    condition;
  Value *                               value;
  enum CompOp                           comp;
  enum AggregateOp                      aggregate_op;
  RelAttrSqlNode *                      rel_attr;
  AggregateUnit *                       aggregate_attr;
  std::vector<AggregateUnit> *          aggregate_attr_list;
  std::vector<AttrInfoSqlNode> *        attr_infos;
  AttrInfoSqlNode *                     attr_info;
  Expression *                          expression;
  std::vector<Expression *> *           expression_list;
  std::vector<std::vector<Value>> *     insertPartList;
  std::vector<Value> *                  value_list;
  std::vector<ConditionSqlNode> *       condition_list;
  UpdateUnit *                          assign;
  std::vector<UpdateUnit> *             assign_list;
  std::vector<RelAttrSqlNode> *         rel_attr_list;
  std::vector<std::string> *            relation_list;
  std::vector<std::string> *            users;
  std::vector<IDNode> *                 join_list;
  OrderBySqlNode *                      order_by;
  std::vector<OrderBySqlNode> *         order_by_list;
  std::vector<std::string> *            index_attr_list;
  char *                                string;
  int                                   number;
  float                                 floats;
}

%token <number> NUMBER
%token <floats> FLOAT
%token <string> DATE_STR
%token <string> ID
%token <string> SSS
//非终结符

/** type 定义了各种解析后的结果输出的是什么类型。类型对应了 union 中的定义的成员变量名称 **/
%type <number>              type
%type <number>              priv
%type <number>              privs
%type <relation_list>       relation_list
%type <users>               users
%type <condition>           condition
%type <value>               value
%type <number>              number
%type <comp>                comp_op
%type <comp>                comp_like
%type <comp>                comp_null
%type <comp>                comp_exist
%type <comp>                comp_in
%type <rel_attr>            rel_attr
%type <attr_infos>          attr_def_list
%type <attr_info>           attr_def
%type <insertPartList>      insertPartList
%type <value_list>          insertPart
%type <join_list>           join_list
%type <join_list>           join_node
%type <join_list>           join_part_list
%type <value_list>          value_list
%type <condition_list>      where
%type <condition_list>      condition_list
%type <aggregate_attr_list> select_attr
%type <order_by_list>       order
%type <order_by>            order_by_field
%type <number>              order_flag
%type <order_by_list>       order_by_list
%type <rel_attr_list>       attr_list
%type <aggregate_op>        aggregate_op
%type <aggregate_attr>      aggregate_attr
%type <aggregate_attr_list> aggregate_attr_list
%type <expression>          expression
%type <expression_list>     expression_list
%type <sql_node>            calc_stmt
%type <sql_node>            select_stmt
%type <sql_node>            grant_stmt
%type <sql_node>            insert_stmt
%type <sql_node>            update_stmt
%type <assign>              assign
%type <assign_list>         assign_list
%type <sql_node>            delete_stmt
%type <sql_node>            use_stmt
%type <sql_node>            create_table_stmt
%type <sql_node>            drop_table_stmt
%type <sql_node>            show_tables_stmt
%type <sql_node>            show_index_stmt
%type <sql_node>            desc_table_stmt
%type <sql_node>            create_index_stmt
%type <sql_node>            drop_index_stmt
%type <sql_node>            sync_stmt
%type <sql_node>            begin_stmt
%type <sql_node>            commit_stmt
%type <sql_node>            rollback_stmt
%type <sql_node>            load_data_stmt
%type <sql_node>            explain_stmt
%type <sql_node>            set_variable_stmt
%type <sql_node>            help_stmt
%type <sql_node>            exit_stmt
%type <sql_node>            command_wrapper
%type <index_attr_list>     index_attr_list
%type <number>              is_unique
%type <string>              is_alias
// commands should be a list but I use a single command instead
%type <sql_node>            commands

%left '+' '-'
%left '*' '/'
%nonassoc UMINUS
%%

commands: command_wrapper opt_semicolon  //commands or sqls. parser starts here.
  {
    std::unique_ptr<ParsedSqlNode> sql_node = std::unique_ptr<ParsedSqlNode>($1);
    sql_result->add_sql_node(std::move(sql_node));
  }
  ;

command_wrapper:
    calc_stmt
  | select_stmt
  | insert_stmt
  | update_stmt
  | delete_stmt
  | grant_stmt
  | use_stmt
  | create_table_stmt
  | drop_table_stmt
  | show_tables_stmt
  | show_index_stmt
  | desc_table_stmt
  | create_index_stmt
  | drop_index_stmt
  | sync_stmt
  | begin_stmt
  | commit_stmt
  | rollback_stmt
  | load_data_stmt
  | explain_stmt
  | set_variable_stmt
  | help_stmt
  | exit_stmt
    ;

exit_stmt:      
    EXIT {
      (void)yynerrs;  // 这么写为了消除yynerrs未使用的告警。如果你有更好的方法欢迎提PR
      $$ = new ParsedSqlNode(SCF_EXIT);
    };

help_stmt:
    HELP {
      $$ = new ParsedSqlNode(SCF_HELP);
    };

sync_stmt:
    SYNC {
      $$ = new ParsedSqlNode(SCF_SYNC);
    }
    ;

begin_stmt:
    TRX_BEGIN  {
      $$ = new ParsedSqlNode(SCF_BEGIN);
    }
    ;

commit_stmt:
    TRX_COMMIT {
      $$ = new ParsedSqlNode(SCF_COMMIT);
    }
    ;

rollback_stmt:
    TRX_ROLLBACK  {
      $$ = new ParsedSqlNode(SCF_ROLLBACK);
    }
    ;

drop_table_stmt:    /*drop table 语句的语法解析树*/
    DROP TABLE ID {
      $$ = new ParsedSqlNode(SCF_DROP_TABLE);
      $$->drop_table.relation_name = $3;
      free($3);
    };

show_tables_stmt:
    SHOW TABLES {
      $$ = new ParsedSqlNode(SCF_SHOW_TABLES);
    }
    ;

show_index_stmt:
    SHOW INDEX FROM ID {
      $$ = new ParsedSqlNode(SCF_SHOW_INDEX);
      $$->show_index.relation_name = $4;
    }
    ;

desc_table_stmt:
    DESC ID  {
      $$ = new ParsedSqlNode(SCF_DESC_TABLE);
      $$->desc_table.relation_name = $2;
      free($2);
    }
    ;

create_index_stmt:    /*create index 语句的语法解析树*/
    CREATE is_unique INDEX ID ON ID LBRACE ID index_attr_list RBRACE
    {
      $$ = new ParsedSqlNode(SCF_CREATE_INDEX);
      CreateIndexSqlNode &create_index = $$->create_index;
      create_index.is_unique = 0==$2 ? false:true;
      create_index.index_name = $4;
      create_index.relation_name = $6;
      if ($9 != nullptr) {
        create_index.attribute_names.swap(*$9);
      }
      create_index.attribute_names.emplace_back($8);
      std::reverse(create_index.attribute_names.begin(), create_index.attribute_names.end());
      // create_index.attribute_name = $8;
      free($4);
      free($6);
      free($8);
      free($9);
    }
    ;
is_unique:
    /* empty */
    {
      $$ = 0;
    }
    | UNIQUE {
      $$ = 1;
    }
    ;
index_attr_list:
    /* empty */
    {
      $$ = nullptr;
    }
    | COMMA ID index_attr_list {
      if ($3 != nullptr) {
        $$ = $3;
      } else {
        $$ = new std::vector<std::string>;
      }
      $$->emplace_back($2);
      free($2);
    }
    ;
drop_index_stmt:      /*drop index 语句的语法解析树*/
    DROP INDEX ID ON ID
    {
      $$ = new ParsedSqlNode(SCF_DROP_INDEX);
      $$->drop_index.index_name = $3;
      $$->drop_index.relation_name = $5;
      free($3);
      free($5);
    }
    ;
grant_stmt:
    GRANT priv privs ON ID relation_list TO ID users
    {
      $$ = new ParsedSqlNode(SCF_GRANT);
      $$->grant.priv = 0;
      if ($3 != -1) {
        $$->grant.priv = $3;
      }
      $$->grant.priv |= $2;
      if ($6 != nullptr) {
        $$->grant.relations.swap(*$6);
        delete $6;
      }
      $$->grant.relations.emplace_back($5);
      if ($9 != nullptr) {
        $$->grant.users.swap(*$9);
        delete $9;
      }
      $$->grant.users.emplace_back($8);
      free($5);
      free($8);
    }
    | GRANT priv privs ON '*' relation_list TO ID users
    {
      $$ = new ParsedSqlNode(SCF_GRANT);
      $$->grant.priv = 0;
      if ($3 != -1) {
        $$->grant.priv = $3;
      }
      $$->grant.priv |= $2;
      if ($6 != nullptr) {
        $$->grant.relations.swap(*$6);
        delete $6;
      }
      $$->grant.relations.emplace_back("*");
      if ($9 != nullptr) {
        $$->grant.users.swap(*$9);
        delete $9;
      }
      $$->grant.users.emplace_back($8);
      free($8);
    }
    ;
privs:
    /* empty */
    {
      $$ = -1;
    }
    | COMMA priv privs
    {
      if ($3 != -1) {
        $$ = $3;
      } else {
        $$ = 0;
      }
      $$ |= $2;
    }
    ;
priv:
    INSERT {$$ = 1;}
    | DELETE {$$ = 1 << 1;}
    | UPDATE {$$ = 1 << 2;}
    | SELECT {$$ = 1 << 3;}
    | DROP {$$ = 1 << 4;}
    ;
relation_list:
    /* empty */
    {
      $$ = nullptr;
    }
    | COMMA ID relation_list
    {
      if ($3 != nullptr) {
        $$ = $3;
      } else {
        $$ = new std::vector<std::string>;
      }
      $$->emplace_back($2);
    }
    ;
users:
    /* empty */
    {
      $$ = nullptr;
    }
    | COMMA ID users
    {
      if ($3 != nullptr) {
        $$ = $3;
      } else {
        $$ = new std::vector<std::string>;
      }
      $$->emplace_back($2);
    }

use_stmt:
    USE ID
    {
      $$ = new ParsedSqlNode(SCF_USE);
      UseSqlNode &use = $$->use;
      use.database = $2;
      free($2);
    }
    ;

create_table_stmt:    /*create table 语句的语法解析树*/
    CREATE TABLE ID LBRACE attr_def attr_def_list RBRACE
    {
      $$ = new ParsedSqlNode(SCF_CREATE_TABLE);
      CreateTableSqlNode &create_table = $$->create_table;
      create_table.relation_name = $3;
      free($3);

      std::vector<AttrInfoSqlNode> *src_attrs = $6;

      if (src_attrs != nullptr) {
        create_table.attr_infos.swap(*src_attrs);
      }
      create_table.attr_infos.emplace_back(*$5);
      std::reverse(create_table.attr_infos.begin(), create_table.attr_infos.end());
      delete $5;
    }
    ;
attr_def_list:
    /* empty */
    {
      $$ = nullptr;
    }
    | COMMA attr_def attr_def_list
    {
      if ($3 != nullptr) {
        $$ = $3;
      } else {
        $$ = new std::vector<AttrInfoSqlNode>;
      }
      $$->emplace_back(*$2);
      delete $2;
    }
    ;
    
attr_def:
    ID type LBRACE number RBRACE NULLABLE 
    {
      $$ = new AttrInfoSqlNode;
      $$->type = (AttrType)$2;
      $$->name = $1;
      $$->length = $4;
      $$->is_nullable = 1;
      free($1);
    }
    | ID type LBRACE number RBRACE NOT NULLABLE
    {
      $$ = new AttrInfoSqlNode;
      $$->type = (AttrType)$2;
      $$->name = $1;
      $$->length = $4;
      $$->is_nullable = 0;
      free($1);
    }
    | ID type LBRACE number RBRACE 
    {
      $$ = new AttrInfoSqlNode;
      $$->type = (AttrType)$2;
      $$->name = $1;
      $$->length = $4;
      $$->is_nullable = 1;
      free($1);
    }
    | ID type NULLABLE
    {
      $$ = new AttrInfoSqlNode;
      $$->type = (AttrType)$2;
      $$->name = $1;
      $$->length = 4;
      $$->is_nullable = 1;
      free($1);
    }
    | ID type NOT NULLABLE
    {
      $$ = new AttrInfoSqlNode;
      $$->type = (AttrType)$2;
      $$->name = $1;
      $$->length = 4;
      $$->is_nullable = 0;
      free($1);
    }
    | ID type
    {
      $$ = new AttrInfoSqlNode;
      $$->type = (AttrType)$2;
      $$->name = $1;
      $$->length = 4;
      $$->is_nullable = 1;
      free($1);
    }
    ;
number:
    NUMBER {$$ = $1;}
    ;
type:
    INT_T      { $$=INTS; }
    | STRING_T { $$=CHARS; }
    | FLOAT_T  { $$=FLOATS; }
    | DATE_T   { $$=DATES; }
    | TEXT_T   { $$=TEXTS; }
    ;
insert_stmt:        /*insert   语句的语法解析树*/
    // INSERT INTO ID VALUES LBRACE value value_list RBRACE
    INSERT INTO ID VALUES insertPart insertPartList
    {
      /*
      $$ = new ParsedSqlNode(SCF_INSERT);
      $$->insertion.relation_name = $3;
      if ($7 != nullptr) {
        $$->insertion.values.swap(*$7);
      }
      $$->insertion.values.emplace_back(*$6);
      std::reverse($$->insertion.values.begin(), $$->insertion.values.end());
      delete $6;
      free($3);
      */
      $$ = new ParsedSqlNode(SCF_INSERT);
      $$->insertion.relation_name = $3;
      if ($6 != nullptr) {

        $$->insertion.insertPartList.swap(*$6);
      }
      $$->insertion.insertPartList.emplace_back(*$5);
      std::reverse($$->insertion.insertPartList.begin(), $$->insertion.insertPartList.end());
      delete $5;
      free($3);
    }
    ;

insertPartList:
    /* empty */
    {
      $$ = nullptr;
    }
    | COMMA insertPart insertPartList  {
      if ($3 != nullptr) {
        $$ = $3;
      } else {
        $$ = new std::vector<vector<Value>>;
      }
      // std::reverse($2->begin(), $2->end());
      $$->emplace_back(*$2);
    }
    ;
insertPart:
    LBRACE value value_list RBRACE {
      $$ = new std::vector<Value>;
      if ($3 != nullptr) {
        $$->insert($$->end(), $3->begin(), $3->end());
      }
      $$->emplace_back(*$2);
      std::reverse($$->begin(), $$->end());
      delete $2;
    }
    ;
value_list:
    /* empty */
    {
      $$ = nullptr;
    }
    | COMMA value value_list  { 
      if ($3 != nullptr) {
        $$ = $3;
      } else {
        $$ = new std::vector<Value>;
      }
      $$->emplace_back(*$2);
      delete $2;
    }
    ;
value:
    NUMBER {
      $$ = new Value((int)$1);
      @$ = @1;
    }
    |FLOAT {
      $$ = new Value((float)$1);
      @$ = @1;
    }
    |DATE_STR {
      char *tmp = common::substr($1,1,strlen($1)-2);
      int date_value = 0;
      int ret = value_init_date(tmp, date_value);
      if (ret != 0) {
          yyerror(&yylloc, "insert的部分出错", sql_result, scanner, "Invalid date");
          YYERROR;
      }
      $$ = new Value(date_value, AttrType::DATES);
      free(tmp);
    }
    |SSS {
      char *tmp = common::substr($1,1,strlen($1)-2);
      $$ = new Value(tmp);
      free(tmp);
    }
    | NULLABLE {
      $$ = new Value();
      $$->set_null();
    }
    ;
    
delete_stmt:    /*  delete 语句的语法解析树*/
    DELETE FROM ID where 
    {
      $$ = new ParsedSqlNode(SCF_DELETE);
      $$->deletion.relation_name = $3;
      if ($4 != nullptr) {
        $$->deletion.conditions.swap(*$4);
        delete $4;
      }
      free($3);
    }
    ;
update_stmt:      /*  update 语句的语法解析树*/
    UPDATE ID SET assign assign_list where
    {
      $$ = new ParsedSqlNode(SCF_UPDATE);
      $$->update.relation_name = $2;
      if ($5 != nullptr) {
        $$->update.update_units = *$5;
        delete $5;
      }
      if ($6 != nullptr) {
        $$->update.conditions.swap(*$6);
        delete $6;
      }
      $$->update.update_units.emplace_back(*$4);
      delete $4;
    }
    ;
assign:
    ID EQ value
    {
      $$ = new UpdateUnit;
      $$->attribute_name = $1;
      $$->value = *$3;
      free($3);
    }
    | ID EQ LBRACE select_stmt RBRACE
    {
      $$ = new UpdateUnit;
      $$->attribute_name = $1;
      $$->sub_select = $4->selection;
      delete $4;
    }
    ;
assign_list:
    /* empty */
    {
      $$ = nullptr;
    }
    | COMMA assign assign_list
    {
      if ($3 != nullptr) {
        $$ = $3;
      } else {
        $$ = new std::vector<UpdateUnit>;
      }
      $$->emplace_back(*$2);
      delete $2;
    };
select_stmt:        /*  select 语句的语法解析树*/
    SELECT select_attr FROM join_node join_list where order
    {
      $$ = new ParsedSqlNode(SCF_SELECT);
      if ($2 != nullptr) {
        std::vector<RelAttrSqlNode> tmp;
        for (auto it = $2->begin(); it != $2->end(); it++) {
          tmp.emplace_back(it->attribute);
        }
        $$->selection.attributes.swap(tmp);
        $$->selection.aggregations.swap(*$2);
        delete $2;
      }

      if ($5 != nullptr) {
        //$$->selection.relations.swap(*$5);
        for (IDNode idNode : *$5) {
          RelationSqlNode tmp;
          if (!idNode.relation_name.empty()) {
            // $$->selection.relations.push_back(idNode.relation_name);
            tmp.relation_name = idNode.relation_name;
          }
          if (!idNode.join_relation_name.empty()) {
            // $$->selection.relations.push_back(idNode.join_relation_name);
            tmp.relation_name = idNode.join_relation_name;
          }
          if (!idNode.relation_alias.empty()) {
            tmp.relation_alias = idNode.relation_alias;
          }
          if (!idNode.join_relation_alias.empty()) {
            tmp.relation_alias = idNode.join_relation_alias;
          }
          $$->selection.relations.emplace_back(tmp);
          if (!idNode.conditions.empty()) {
            std::copy(idNode.conditions.begin(), idNode.conditions.end(), std::back_inserter($$->selection.conditions));
          }
        }
        delete $5;
      }
      if ($4 != nullptr) {
        //$$->selection.relations.swap(*$4);
        RelationSqlNode tmp;
        for (auto it = $4->rbegin(); it != $4->rend(); it++) {
          if (!it->relation_name.empty()) {
            // $$->selection.relations.push_back(idNode.relation_name);
            tmp.relation_name = it->relation_name;
          }
          if (!it->join_relation_name.empty()) {
            // $$->selection.relations.push_back(idNode.join_relation_name);
            tmp.relation_name = it->join_relation_name;
          }
          if (!it->relation_alias.empty()) {
            tmp.relation_alias = it->relation_alias;
          }
          if (!it->join_relation_alias.empty()) {
            tmp.relation_alias = it->join_relation_alias;
          }
          $$->selection.relations.emplace_back(tmp);
          if (!it->conditions.empty()) {
            std::copy(it->conditions.begin(), it->conditions.end(), std::back_inserter($$->selection.conditions));
          }
        }
        delete $4;
      }

      if ($6 != nullptr) {
        std::copy($6->begin(), $6->end(), std::back_inserter($$->selection.conditions));
        delete $6;
      }

      if ($7 != nullptr){
        $$->selection.orders.swap(*$7);
        delete $7;
      }
    }
    ;
calc_stmt:
    CALC expression_list
    {
      $$ = new ParsedSqlNode(SCF_CALC);
      std::reverse($2->begin(), $2->end());
      $$->calc.expressions.swap(*$2);
      delete $2;
    }
    ;
order:
    /* empty */
    {
      $$ = nullptr;
    }
    | ORDER BY order_by_field order_by_list
    {
      $$ = new std::vector<OrderBySqlNode>;
      if ($4 != nullptr) {
        $$->swap(*$4);
      }
      $$->emplace_back(*$3);
      delete $3;
      delete $4;
    }
order_by_list:
/* empty */
    {
      $$ = nullptr;
    }
    | COMMA order_by_field order_by_list
    {
      if ($3 != nullptr) {
        $$ = $3;
      } else {
        $$ = new std::vector<OrderBySqlNode>;
      }
      $$->emplace_back(*$2);
      delete $2;
    }
order_by_field:
    rel_attr order_flag
    {
      $$ = new OrderBySqlNode;
      $$->rel = *$1;
      $$->flag = $2;
      delete $1;
    }

order_flag:
   /* empty */
   {
    $$ = 0;
   }
   | ASC { $$ = 0; }
   | DESC { $$ = 1; }
expression_list:
    expression
    {
      $$ = new std::vector<Expression*>;
      $$->emplace_back($1);
    }
    | expression COMMA expression_list
    {
      if ($3 != nullptr) {
        $$ = $3;
      } else {
        $$ = new std::vector<Expression *>;
      }
      $$->emplace_back($1);
    }
    ;
expression:
    expression '+' expression {
      $$ = create_arithmetic_expression(ArithmeticExpr::Type::ADD, $1, $3, sql_string, &@$);
    }
    | expression '-' expression {
      $$ = create_arithmetic_expression(ArithmeticExpr::Type::SUB, $1, $3, sql_string, &@$);
    }
    | expression '*' expression {
      $$ = create_arithmetic_expression(ArithmeticExpr::Type::MUL, $1, $3, sql_string, &@$);
    }
    | expression '/' expression {
      $$ = create_arithmetic_expression(ArithmeticExpr::Type::DIV, $1, $3, sql_string, &@$);
    }
    | LBRACE expression RBRACE {
      $$ = $2;
      $$->set_name(token_name(sql_string, &@$));
    }
    | '-' expression %prec UMINUS {
      $$ = create_arithmetic_expression(ArithmeticExpr::Type::NEGATIVE, $2, nullptr, sql_string, &@$);
    }
    | value {
      $$ = new ValueExpr(*$1);
      $$->set_name(token_name(sql_string, &@$));
      delete $1;
    }
    ;

select_attr:
    '*' {
      $$ = new std::vector<AggregateUnit>;
      RelAttrSqlNode attr;
      attr.relation_name  = "";
      attr.attribute_name = "*";
      AggregateUnit tmp;
      tmp.attribute = attr;
      tmp.aggregation = NO_AGGR;
      $$->emplace_back(tmp);
    }
    | rel_attr is_alias attr_list {
      $$ = new std::vector<AggregateUnit>;
      if ($3 != nullptr) {
        for (auto it = $3->begin(); it != $3->end(); it++) {
          AggregateUnit tmp;
          tmp.attribute = *it;
          tmp.aggregation = NO_AGGR;
          $$->emplace_back(tmp);
        }
      }
      if ($2 != nullptr) {
        $1->attribute_alias = $2;
        free($2);
      }
      AggregateUnit tmp;
      tmp.attribute = *$1;
      tmp.aggregation = NO_AGGR;
      $$->emplace_back(tmp);
      delete $1;
    }
    | aggregate_attr aggregate_attr_list {
      if ($2 != nullptr) {
        $$ = $2;
      } else {
        $$ = new std::vector<AggregateUnit>;
      }
      $$->emplace_back(*$1);
      delete $1;
    }
    ;
is_alias:
    /* empty */
    {
      $$ = nullptr;
    }
    | ID {
      $$ = $1;
    }
    | AS ID {
      $$ = $2;
    }
    ;
aggregate_attr:
    aggregate_op LBRACE '*' RBRACE {
      $$ = new AggregateUnit;
      RelAttrSqlNode attr;
      attr.relation_name  = "";
      attr.attribute_name = "*";
      $$->attribute = attr;
      $$->aggregation = $1;
    }
    | aggregate_op LBRACE rel_attr RBRACE {
      $$ = new AggregateUnit;
      $$->attribute = *$3;
      $$->aggregation = $1;
  	};
aggregate_op: 
	  COUNT { $$ = COUNT_OP; }
	| AVG   { $$ = AVG_OP; }
	| MAX   { $$ = MAX_OP; }
	| MIN   { $$ = MIN_OP; }
	| SUM   { $$ = SUM_OP; };

aggregate_attr_list:
    /* empty */
    {
      $$ = nullptr;
    }
	|COMMA aggregate_attr aggregate_attr_list {
    if ($3 != nullptr) {
        $$ = $3;
      } else {
        $$ = new std::vector<AggregateUnit>;
      }
      $$->emplace_back(*$2);
      delete $2;
	}


rel_attr:
    ID {
      $$ = new RelAttrSqlNode;
      $$->attribute_name = $1;
      $$->attribute_alias = "";
      free($1);
    }
    | ID DOT ID {
      $$ = new RelAttrSqlNode;
      $$->relation_name  = $1;
      $$->attribute_name = $3;
      free($1);
      free($3);
    }
    ;

attr_list:
    /* empty */
    {
      $$ = nullptr;
    }
    | COMMA rel_attr is_alias attr_list {
      if ($4 != nullptr) {
        $$ = $4;
      } else {
        $$ = new std::vector<RelAttrSqlNode>;
      }
      if ($3 != nullptr) {
        $2->attribute_alias = $3;
        free($3);
      }
      $$->emplace_back(*$2);
      delete $2;
    }
    ;

join_list:
    /* empty */
    {
      $$ = nullptr;
    }
    | COMMA join_node join_list {
      if ($3 != nullptr) {
        $$ = $3;
      } else {
        $$ = new std::vector<IDNode>;
      }

      //$$->emplace_back(*$2);
      //std::copy($2->begin(), $2->end(), std::back_inserter(*$$));
      $$->insert($$->end(), $2->begin(), $2->end());
      free($2);
    }
    ;
join_node:
    ID is_alias join_part_list {
      $$ = new std::vector<IDNode>;
      if ($3 != nullptr) {
        //$$ = new std::vector<IDNode>(*$2);
        std::copy($3->begin(), $3->end(), std::back_inserter(*$$));
      }

      IDNode idNode;
      idNode.relation_name = $1;
      if ($2 != nullptr) {
        idNode.relation_alias = $2;
        free($2);
      }
      idNode.join_relation_name = "";
      $$->emplace_back(idNode);

      std::reverse($$->begin(), $$->end());

      free($1);
    }
    ;
join_part_list:
    /* empty */
    {
      $$ = nullptr;
    }
    /*| join_part_node join_part_list {
      if ($2 != nullptr) {
        $$ = $2;
      } else {
        $$ = new std::vector<IDNode>;
      }
      $$->emplace_back(*$1);
      free($2);
    }*/
    | INNER JOIN ID is_alias ON condition_list join_part_list {
      if ($7 != nullptr) {
        $$ = $7;
      } else {
        $$ = new std::vector<IDNode>;
      }
      IDNode idNode;
      idNode.relation_name = "";
      idNode.join_relation_name = $3;
      if ($4 != nullptr) {
        idNode.join_relation_alias = $4;
        free($4);
      }
      idNode.conditions.swap(*$6);
      $$->emplace_back(idNode);
      free($3);
    }
    ;

where:
    /* empty */
    {
      $$ = nullptr;
    }
    | WHERE condition_list {
      $$ = $2;  
    }
    ;
condition_list:
    /* empty */
    {
      $$ = nullptr;
    }
    | condition {
      $$ = new std::vector<ConditionSqlNode>;
      $$->emplace_back(*$1);
      delete $1;
    }
    | condition AND condition_list {
      $$ = $3;
      $$->emplace_back(*$1);
      delete $1;
    }
    ;
condition:
    rel_attr comp_op value
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 1;
      $$->left_attr = *$1;
      $$->right_is_attr = 0;
      $$->right_value = *$3;
      $$->comp = $2;

      delete $1;
      delete $3;
    }
    | rel_attr comp_like value
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 1;
      $$->left_attr = *$1;
      $$->right_is_attr = 0;
      $$->right_value = *$3;
      $$->comp = $2;

      delete $1;
      delete $3;
    }
    | rel_attr comp_op LBRACE select_stmt RBRACE
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 1;
      $$->left_attr = *$1;
      $$->right_is_attr = 0;
      $$->right_sub_select = &($4->selection);
      $$->comp = $2;

      delete $1;
    }
    | value comp_op value 
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 0;
      $$->left_value = *$1;
      $$->right_is_attr = 0;
      $$->right_value = *$3;
      $$->comp = $2;

      delete $1;
      delete $3;
    }
    | value comp_op LBRACE select_stmt RBRACE
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 0;
      $$->left_value = *$1;
      $$->right_is_attr = 0;
      $$->right_sub_select = &($4->selection);
      $$->comp = $2;

      delete $1;
    }
    | LBRACE select_stmt RBRACE comp_op value
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 0;
      $$->left_sub_select = &($2->selection);
      $$->right_is_attr = 0;
      $$->right_value = *$5;
      $$->comp = $4;
      delete $5;
    }
    | LBRACE select_stmt RBRACE comp_op LBRACE select_stmt RBRACE
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 0;
      $$->left_sub_select = &($2->selection);
      $$->right_is_attr = 0;
      $$->right_sub_select = &($6->selection);
      $$->comp = $4;
    }
    | rel_attr comp_op rel_attr
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 1;
      $$->left_attr = *$1;
      $$->right_is_attr = 1;
      $$->right_attr = *$3;
      $$->comp = $2;

      delete $1;
      delete $3;
    }
    | rel_attr comp_null
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 1;
      $$->left_attr = *$1;
      $$->right_is_attr = 0;
      Value *value = new Value();
      value->set_null();
      $$->right_value = *value;
      $$->comp = $2;
      delete value;
      delete $1;
    }
    | value comp_op rel_attr
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 0;
      $$->left_value = *$1;
      $$->right_is_attr = 1;
      $$->right_attr = *$3;
      $$->comp = $2;

      delete $1;
      delete $3;
    }
    | LBRACE select_stmt RBRACE comp_op rel_attr
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 0;
      $$->left_sub_select = &($2->selection);
      $$->right_is_attr = 1;
      $$->right_attr = *$5;
      $$->comp = $4;

      delete $5;
    }
    | value comp_null
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 0;
      $$->left_value = *$1;
      $$->right_is_attr = 0;
      Value *value = new Value();
      value->set_null();
      $$->right_value = *value;
      $$->comp = $2;
      delete value;
      delete $1;
    }
    | comp_exist LBRACE select_stmt RBRACE
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 0;
      Value *value = new Value();
      value->set_int(1);
      $$->left_value = *value;
      $$->right_is_attr = 0;
      $$->right_sub_select = &($3->selection);
      $$->comp = $1;
      delete value;
    }
    | rel_attr comp_in LBRACE select_stmt RBRACE
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 1;
      $$->left_attr = *$1;
      $$->right_is_attr = 0;
      $$->right_sub_select = &($4->selection);
      $$->comp = $2;
      delete $1;
    }
    | rel_attr comp_in LBRACE value value_list RBRACE 
    {
      $$ = new ConditionSqlNode;
      $$->left_is_attr = 1;
      $$->left_attr = *$1;
      $$->right_is_attr = 0;
      $$->comp = $2;
      if ($5 != nullptr) {
        $$->in_value_list.swap(*$5);
      }
      $$->in_value_list.emplace_back(*$4);
    }
    ;

comp_op:
      EQ { $$ = EQUAL_TO; }
    | LT { $$ = LESS_THAN; }
    | GT { $$ = GREAT_THAN; }
    | LE { $$ = LESS_EQUAL; }
    | GE { $$ = GREAT_EQUAL; }
    | NE { $$ = NOT_EQUAL; }
    ;
comp_like:
    LIKE {$$ = LIKE_OP; }   
    | NOT LIKE {$$ = NOT_LIKE_OP; }
comp_null: 
    IS NULLABLE {$$ = IS_NULL_OP; }
    | IS NOT NULLABLE {$$ = IS_NOT_NULL_OP;}
comp_exist:
    EXISTS {$$ = EXISTS_OP; }
    | NOT EXISTS {$$ = NOT_EXISTS_OP; }
comp_in:
    IN {$$ = IN_OP; }
    | NOT IN {$$ = NOT_IN_OP; }
load_data_stmt:
    LOAD DATA INFILE SSS INTO TABLE ID 
    {
      char *tmp_file_name = common::substr($4, 1, strlen($4) - 2);
      
      $$ = new ParsedSqlNode(SCF_LOAD_DATA);
      $$->load_data.relation_name = $7;
      $$->load_data.file_name = tmp_file_name;
      free($7);
      free(tmp_file_name);
    }
    ;

explain_stmt:
    EXPLAIN command_wrapper
    {
      $$ = new ParsedSqlNode(SCF_EXPLAIN);
      $$->explain.sql_node = std::unique_ptr<ParsedSqlNode>($2);
    }
    ;

set_variable_stmt:
    SET ID EQ value
    {
      $$ = new ParsedSqlNode(SCF_SET_VARIABLE);
      $$->set_variable.name  = $2;
      $$->set_variable.value = *$4;
      free($2);
      delete $4;
    }
    ;

opt_semicolon: /*empty*/
    | SEMICOLON
    ;
%%
//_____________________________________________________________________
extern void scan_string(const char *str, yyscan_t scanner);

int sql_parse(const char *s, ParsedSqlResult *sql_result) {
  yyscan_t scanner;
  yylex_init(&scanner);
  scan_string(s, scanner);
  int result = yyparse(s, sql_result, scanner);
  yylex_destroy(scanner);
  return result;
}
